home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 2: CDPD 1 / Almathera Ten on Ten - Disc 2: CDPD 1.iso / pd / 051-075 / 074 / less / prim.c < prev    next >
C/C++ Source or Header  |  1995-03-13  |  12KB  |  690 lines

  1. /*
  2.  * Primitives for displaying the file on the screen.
  3.  */
  4.  
  5. #include "less.h"
  6. #include "position.h"
  7.  
  8. public int hit_eof;     /* Keeps track of how many times we hit end of file */
  9.  
  10. extern int quiet;
  11. extern int top_search;
  12. extern int top_scroll;
  13. extern int back_scroll;
  14. extern int sc_width, sc_height;
  15. extern int sigs;
  16. extern char *line;
  17. extern char *first_cmd;
  18.  
  19. /*
  20.  * Sound the bell to indicate he is trying to move past end of file.
  21.  */
  22.     static void
  23. eof_bell()
  24. {
  25.     if (quiet == NOT_QUIET)
  26.         bell();
  27.     else
  28.         vbell();
  29. }
  30.  
  31. /*
  32.  * Check to see if the end of file is currently "displayed".
  33.  */
  34.     static void
  35. eof_check()
  36. {
  37.     POSITION pos;
  38.  
  39.     /*
  40.      * If the bottom line is empty, we are at EOF.
  41.      * If the bottom line ends at the file length,
  42.      * we must be just at EOF.
  43.      */
  44.     pos = position(BOTTOM_PLUS_ONE);
  45.     if (pos == NULL_POSITION || pos == ch_length())
  46.         hit_eof++;
  47. }
  48.  
  49. /*
  50.  * Display n lines, scrolling forward, 
  51.  * starting at position pos in the input file.
  52.  * "force" means display the n lines even if we hit end of file.
  53.  * "only_last" means display only the last screenful if n > screen size.
  54.  */
  55.     static void
  56. forw(n, pos, force, only_last)
  57.     register int n;
  58.     POSITION pos;
  59.     int force;
  60.     int only_last;
  61. {
  62.     int eof = 0;
  63.     int nlines = 0;
  64.     int repaint_flag;
  65.  
  66.     /*
  67.      * repaint_flag tells us not to display anything till the end, 
  68.      * then just repaint the entire screen.
  69.      */
  70.     repaint_flag = (only_last && n > sc_height-1);
  71.  
  72.     if (!repaint_flag)
  73.     {
  74.         if (top_scroll && n >= sc_height - 1)
  75.         {
  76.             /*
  77.              * Start a new screen.
  78.              * {{ This is not really desirable if we happen
  79.              *    to hit eof in the middle of this screen,
  80.              *    but we don't know if that will happen now. }}
  81.              */
  82.             clear();
  83.             home();
  84.             force = 1;
  85.         } else
  86.         {
  87.             lower_left();
  88.             clear_eol();
  89.         }
  90.  
  91.         if (pos != position(BOTTOM_PLUS_ONE))
  92.         {
  93.             /*
  94.              * This is not contiguous with what is
  95.              * currently displayed.  Clear the screen image 
  96.              * (position table) and start a new screen.
  97.              */
  98.             pos_clear();
  99.             add_forw_pos(pos);
  100.             force = 1;
  101.             if (top_scroll)
  102.             {
  103.                 clear();
  104.                 home();
  105.             } else
  106.             {
  107.                 puts("...skipping...\n");
  108.             }
  109.         }
  110.     }
  111.  
  112.     while (--n >= 0)
  113.     {
  114.         /*
  115.          * Read the next line of input.
  116.          */
  117.         pos = forw_line(pos);
  118.         if (pos == NULL_POSITION)
  119.         {
  120.             /*
  121.              * End of file: stop here unless the top line 
  122.              * is still empty, or "force" is true.
  123.              */
  124.             eof = 1;
  125.             if (!force && position(TOP) != NULL_POSITION)
  126.                 break;
  127.             line = NULL;
  128.         }
  129.         /*
  130.          * Add the position of the next line to the position table.
  131.          * Display the current line on the screen.
  132.          */
  133.         add_forw_pos(pos);
  134.         nlines++;
  135.         if (!repaint_flag)
  136.             put_line();
  137.     }
  138.  
  139.     if (eof)
  140.         hit_eof++;
  141.     else
  142.         eof_check();
  143.     if (nlines == 0)
  144.         eof_bell();
  145.     else if (repaint_flag)
  146.         repaint();
  147. }
  148.  
  149. /*
  150.  * Display n lines, scrolling backward.
  151.  */
  152.     static void
  153. back(n, pos, force, only_last)
  154.     register int n;
  155.     POSITION pos;
  156.     int force;
  157.     int only_last;
  158. {
  159.     int nlines = 0;
  160.     int repaint_flag;
  161.  
  162.     repaint_flag = (n > back_scroll || (only_last && n > sc_height-1));
  163.     hit_eof = 0;
  164.     while (--n >= 0)
  165.     {
  166.         /*
  167.          * Get the previous line of input.
  168.          */
  169.         pos = back_line(pos);
  170.         if (pos == NULL_POSITION)
  171.         {
  172.             /*
  173.              * Beginning of file: stop here unless "force" is true.
  174.              */
  175.             if (!force)
  176.                 break;
  177.             line = NULL;
  178.         }
  179.         /*
  180.          * Add the position of the previous line to the position table.
  181.          * Display the line on the screen.
  182.          */
  183.         add_back_pos(pos);
  184.         nlines++;
  185.         if (!repaint_flag)
  186.         {
  187.             home();
  188.             add_line();
  189.             put_line();
  190.         }
  191.     }
  192.  
  193.     eof_check();
  194.     if (nlines == 0)
  195.         eof_bell();
  196.     else if (repaint_flag)
  197.         repaint();
  198. }
  199.  
  200. /*
  201.  * Display n more lines, forward.
  202.  * Start just after the line currently displayed at the bottom of the screen.
  203.  */
  204.     public void
  205. forward(n, only_last)
  206.     int n;
  207.     int only_last;
  208. {
  209.     POSITION pos;
  210.  
  211.     pos = position(BOTTOM_PLUS_ONE);
  212.     if (pos == NULL_POSITION)
  213.     {
  214.         eof_bell();
  215.         hit_eof++;
  216.         return;
  217.     }
  218.     forw(n, pos, 0, only_last);
  219. }
  220.  
  221. /*
  222.  * Display n more lines, backward.
  223.  * Start just before the line currently displayed at the top of the screen.
  224.  */
  225.     public void
  226. backward(n, only_last)
  227.     int n;
  228.     int only_last;
  229. {
  230.     POSITION pos;
  231.  
  232.     pos = position(TOP);
  233.     if (pos == NULL_POSITION)
  234.     {
  235.         /* 
  236.          * This will almost never happen,
  237.          * because the top line is almost never empty. 
  238.          */
  239.         eof_bell();
  240.         return;   
  241.     }
  242.     back(n, pos, 0, only_last);
  243. }
  244.  
  245. /*
  246.  * Repaint the screen, starting from a specified position.
  247.  */
  248.     static void
  249. prepaint(pos)   
  250.     POSITION pos;
  251. {
  252.     hit_eof = 0;
  253.     forw(sc_height-1, pos, 0, 0);
  254. }
  255.  
  256. /*
  257.  * Repaint the screen.
  258.  */
  259.     public void
  260. repaint()
  261. {
  262.     /*
  263.      * Start at the line currently at the top of the screen
  264.      * and redisplay the screen.
  265.      */
  266.     prepaint(position(TOP));
  267. }
  268.  
  269. /*
  270.  * Jump to the end of the file.
  271.  * It is more convenient to paint the screen backward,
  272.  * from the end of the file toward the beginning.
  273.  */
  274.     public void
  275. jump_forw()
  276. {
  277.     POSITION pos;
  278.  
  279.     if (ch_end_seek())
  280.     {
  281.         error("Cannot seek to end of file");
  282.         return;
  283.     }
  284.     pos = ch_tell();
  285.     clear();
  286.     pos_clear();
  287.     add_back_pos(pos);
  288.     back(sc_height - 1, pos, 0, 0);
  289. }
  290.  
  291. /*
  292.  * Jump to line n in the file.
  293.  */
  294.     public void
  295. jump_back(n)
  296.     register int n;
  297. {
  298.     register int c;
  299.  
  300.     /*
  301.      * This is done the slow way, by starting at the beginning
  302.      * of the file and counting newlines.
  303.      */
  304.     if (ch_seek((POSITION)0))
  305.     {
  306.         /* 
  307.          * Probably a pipe with beginning of file no longer buffered. 
  308.          */
  309.         error("Cannot get to beginning of file");
  310.         return;
  311.     }
  312.  
  313.     /*
  314.      * Start counting lines.
  315.      */
  316.     while (--n > 0)
  317.     {
  318.         while ((c = ch_forw_get()) != '\n')
  319.             if (c == EOF)
  320.             {
  321.                 error("File is not that long");
  322.                 /* {{ Maybe tell him how long it is? }} */
  323.                 return;
  324.             }
  325.     }
  326.  
  327.     /*
  328.      * Finally found the place to start.
  329.      * Clear and redisplay the screen from there.
  330.      *
  331.      * {{ We *could* figure out if the new position is 
  332.      *    close enough to just scroll there without clearing
  333.      *    the screen, but it's not worth it. }}
  334.      */
  335.     prepaint(ch_tell());
  336. }
  337.  
  338. /*
  339.  * Jump to a specified percentage into the file.
  340.  * This is a poor compensation for not being able to
  341.  * quickly jump to a specific line number.
  342.  */
  343.     public void
  344. jump_percent(percent)
  345.     int percent;
  346. {
  347.     POSITION pos, len;
  348.  
  349.     /*
  350.      * Determine the position in the file
  351.      * (the specified percentage of the file's length).
  352.      */
  353.     if ((len = ch_length()) == NULL_POSITION)
  354.     {
  355.         error("Don't know length of file");
  356.         return;
  357.     }
  358.     pos = (percent * len) / 100;
  359.     jump_loc(pos);
  360. }
  361.  
  362.     public void
  363. jump_loc(pos)
  364.     POSITION pos;
  365. {
  366.     register int c;
  367.     register int nline;
  368.     POSITION tpos;
  369.  
  370.     /*
  371.      * See if the desired line is BEFORE the currently
  372.      * displayed screen.  If so, see if it is close enough 
  373.      * to scroll backwards to it.
  374.      */
  375.     tpos = position(TOP);
  376.     if (pos < tpos)
  377.     {
  378.         for (nline = 1;  nline <= back_scroll;  nline++)
  379.         {
  380.             tpos = back_line(tpos);
  381.             if (tpos == NULL_POSITION || tpos <= pos)
  382.             {
  383.                 back(nline, position(TOP), 1, 0);
  384.                 return;
  385.             }
  386.         }
  387.     } else if ((nline = onscreen(pos)) >= 0)
  388.     {
  389.         /*
  390.          * The line is currently displayed.  
  391.          * Just scroll there.
  392.          */
  393.         forw(nline, position(BOTTOM_PLUS_ONE), 1, 0);
  394.         return;
  395.     }
  396.  
  397.     /*
  398.      * Line is not on screen.
  399.      * Back up to the beginning of the current line.
  400.      */
  401.     if (ch_seek(pos))
  402.     {
  403.         error("Cannot seek to that position");
  404.         return;
  405.     }
  406.     while ((c = ch_back_get()) != '\n' && c != EOF)
  407.         ;
  408.     if (c == '\n')
  409.         (void) ch_forw_get();
  410.  
  411.     /*
  412.      * Clear and paint the screen.
  413.      */
  414.     prepaint(ch_tell());
  415. }
  416.  
  417. /*
  418.  * The table of marks.
  419.  * A mark is simply a position in the file.
  420.  */
  421. static POSITION marks[26];
  422.  
  423. /*
  424.  * Initialize the mark table to show no marks are set.
  425.  */
  426.     public void
  427. init_mark()
  428. {
  429.     int i;
  430.  
  431.     for (i = 0;  i < 26;  i++)
  432.         marks[i] = NULL_POSITION;
  433. }
  434.  
  435. /*
  436.  * See if a mark letter is valid (between a and z).
  437.  */
  438.     static int
  439. badmark(c)
  440.     int c;
  441. {
  442.     if (c < 'a' || c > 'z')
  443.     {
  444.         error("Choose a letter between 'a' and 'z'");
  445.         return (1);
  446.     }
  447.     return (0);
  448. }
  449.  
  450. /*
  451.  * Set a mark.
  452.  */
  453.     public void
  454. setmark(c)
  455.     int c;
  456. {
  457.     if (badmark(c))
  458.         return;
  459.     marks[c-'a'] = position(TOP);
  460. }
  461.  
  462. /*
  463.  * Go to a previously set mark.
  464.  */
  465.     public void
  466. gomark(c)
  467.     int c;
  468. {
  469.     POSITION pos;
  470.  
  471.     if (badmark(c))
  472.         return;
  473.     if ((pos = marks[c-'a']) == NULL_POSITION)
  474.         error("mark not set");
  475.     else
  476.         jump_loc(pos);
  477. }
  478.  
  479. /*
  480.  * Search for the n-th occurence of a specified pattern, 
  481.  * either forward (direction == '/'), or backwards (direction == '?').
  482.  */
  483.     public void
  484. search(direction, pattern, n)
  485.     int direction;
  486.     char *pattern;
  487.     register int n;
  488. {
  489.     register int search_forward = (direction == '/');
  490.     POSITION pos, linepos;
  491.  
  492. #if RECOMP
  493.     char *re_comp();
  494.     char *errmsg;
  495.  
  496.     /*
  497.      * (re_comp handles a null pattern internally, 
  498.      *  so there is no need to check for a null pattern here.)
  499.      */
  500.     if ((errmsg = re_comp(pattern)) != NULL)
  501.     {
  502.         error(errmsg);
  503.         return;
  504.     }
  505. #else
  506. #if REGCMP
  507.     char *regcmp();
  508.     static char *cpattern = NULL;
  509.  
  510.     if (pattern == NULL || *pattern == '\0')
  511.     {
  512.         /*
  513.          * A null pattern means use the previous pattern.
  514.          * The compiled previous pattern is in cpattern, so just use it.
  515.          */
  516.         if (cpattern == NULL)
  517.         {
  518.             error("No previous regular expression");
  519.             return;
  520.         }
  521.     } else
  522.     {
  523.         /*
  524.          * Otherwise compile the given pattern.
  525.          */
  526.         char *s;
  527.         if ((s = regcmp(pattern, 0)) == NULL)
  528.         {
  529.             error("Invalid pattern");
  530.             return;
  531.         }
  532.         if (cpattern != NULL)
  533.             free(cpattern);
  534.         cpattern = s;
  535.     }
  536. #else
  537.     static char lpbuf[100];
  538.     static char *last_pattern = NULL;
  539.  
  540.     if (pattern == NULL || *pattern == '\0')
  541.     {
  542.         /*
  543.          * Null pattern means use the previous pattern.
  544.          */
  545.         if (last_pattern == NULL)
  546.         {
  547.             error("No previous regular expression");
  548.             return;
  549.         }
  550.         pattern = last_pattern;
  551.     } else
  552.     {
  553.         strcpy(lpbuf, pattern);
  554.         last_pattern = lpbuf;
  555.     }
  556. #endif
  557. #endif
  558.  
  559.     /*
  560.      * Figure out where to start the search.
  561.      */
  562.  
  563.     if (position(TOP) == NULL_POSITION)
  564.     {
  565.         /*
  566.          * Nothing is currently displayed.
  567.          * Start at the beginning of the file.
  568.          * (This case is mainly for first_cmd searches,
  569.          * for example, "+/xyz" on the command line.)
  570.          */
  571.         pos = (POSITION)0;
  572.     } else if (!search_forward)
  573.     {
  574.         /*
  575.          * Backward search: start just before the top line
  576.          * displayed on the screen.
  577.          */
  578.         pos = position(TOP);
  579.     } else if (top_search)
  580.     {
  581.         /*
  582.          * Forward search and "start from top".
  583.          * Start at the second line displayed on the screen.
  584.          */
  585.         pos = position(TOP_PLUS_ONE);
  586.     } else
  587.     {
  588.         /*
  589.          * Forward search but don't "start from top".
  590.          * Start just after the bottom line displayed on the screen.
  591.          */
  592.         pos = position(BOTTOM_PLUS_ONE);
  593.     }
  594.  
  595.     if (pos == NULL_POSITION)
  596.     {
  597.         /*
  598.          * Can't find anyplace to start searching from.
  599.          */
  600.         error("Nothing to search");
  601.         return;
  602.     }
  603.  
  604.     for (;;)
  605.     {
  606.         /*
  607.          * Get lines until we find a matching one or 
  608.          * until we hit end-of-file (or beginning-of-file 
  609.          * if we're going backwards).
  610.          */
  611.         if (sigs)
  612.             /*
  613.              * A signal aborts the search.
  614.              */
  615.             return;
  616.  
  617.         if (search_forward)
  618.         {
  619.             /*
  620.              * Read the next line, and save the 
  621.              * starting position of that line in linepos.
  622.              */
  623.             linepos = pos;
  624.             pos = forw_raw_line(pos);
  625.         } else
  626.         {
  627.             /*
  628.              * Read the previous line and save the
  629.              * starting position of that line in linepos.
  630.              */
  631.             pos = back_raw_line(pos);
  632.             linepos = pos;
  633.         }
  634.  
  635.         if (pos == NULL_POSITION)
  636.         {
  637.             /*
  638.              * We hit EOF/BOF without a match.
  639.              */
  640.             error("Pattern not found");
  641.             return;
  642.         }
  643.  
  644.         /*
  645.          * Test the next line to see if we have a match.
  646.          * This is done in a variety of ways, depending
  647.          * on what pattern matching functions are available.
  648.          */
  649. #if REGCMP
  650.         if ( (regex(cpattern, line) != NULL)
  651. #else
  652. #if RECOMP
  653.         if ( (re_exec(line) == 1)
  654. #else
  655.         if ( (match(pattern, line))
  656. #endif
  657. #endif
  658.                 && (--n <= 0) )
  659.             /*
  660.              * Found the matching line.
  661.              */
  662.             break;
  663.     }
  664.     jump_loc(linepos);
  665. }
  666.  
  667. #if (!REGCMP) && (!RECOMP)
  668. /*
  669.  * We have neither regcmp() nor re_comp().
  670.  * We use this function to do simple pattern matching.
  671.  * It supports no metacharacters like *, etc.
  672.  */
  673.     static int
  674. match(pattern, buf)
  675.     char *pattern, *buf;
  676. {
  677.     register char *pp, *lp;
  678.  
  679.     for ( ;  *buf != '\0';  buf++)
  680.     {
  681.         for (pp = pattern, lp = buf;  *pp == *lp;  pp++, lp++)
  682.             if (*pp == '\0' || *lp == '\0')
  683.                 break;
  684.         if (*pp == '\0')
  685.             return (1);
  686.     }
  687.     return (0);
  688. }
  689. #endif
  690.